﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

namespace Linxiao.Utility.Library.RSACryptoService
{
    /// <summary>
    /// A RSA helper class which wraps RSACrytoServiceProvider
    /// </summary>
    public class RSACryptoService
    {
        /// <summary>
        /// Encrypt a string and returned the encrypted string
        /// </summary>
        /// <param name="stringToEncrypt">The text to encrypt</param>
        /// <param name="keyContainer">The name of the key container which stores the RSA key-pair. it is a good practice to store keys in key container</param>
        /// <param name="machineLevelKeyContainer">Whether the key container is at machine-level or user-level</param>
        /// <returns>The encrypted string</returns>
        public static string Encrypt(string stringToEncrypt, string keyContainer, bool machineLevelKeyContainer)
        {
            //set key container is at machine-level or user-level
            RSACryptoServiceProvider.UseMachineKeyStore = machineLevelKeyContainer;
            //Encode string to byte array
            byte[] bData = Encoding.Unicode.GetBytes(stringToEncrypt);

            //Get public key from key container. if the key does not exist, create a new key pair.
            byte[] publicKey = GetKeyFromContainer(keyContainer, false);
            string publicKeystring = Convert.ToBase64String(publicKey);

            //Encrypt the string with the public key
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.ImportCspBlob(publicKey);
            byte[] encryptedData = rsa.Encrypt(bData, false);

            //return the encrypted string
            return Convert.ToBase64String(encryptedData);

        }

        /// <summary>
        /// Decrypt a string
        /// </summary>
        /// <param name="stringToDecrypt">The string to decrypt</param>
        /// <param name="keyContainer">The name of the key container which stores the RSA key-pair.  it is a good practice to store keys in key container</param>
        /// <param name="machineLevelKeyContainer">Whether the key container is at machine-level or user-level</param>
        /// <returns>The decrypted string</returns>
        public static string Decrypt(string stringToDecrypt, string keyContainer, bool machineLevelKeyContainer)
        {
            //set key container is at machine-level or user-level
            RSACryptoServiceProvider.UseMachineKeyStore = machineLevelKeyContainer;

            //retrive the private key from key container
            byte[] privateKey = GetKeyFromContainer(keyContainer, true);

            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();

            //Decrypt the string with the private key
            byte[] decrypDate = Convert.FromBase64String(stringToDecrypt);
            rsa.ImportCspBlob(privateKey);
            byte[] decrypedDate = rsa.Decrypt(decrypDate, false);

            //return the decrypted string
            return Encoding.Unicode.GetString(decrypedDate);
        }

        /// <summary>
        /// A protected method to retrive keys from key container
        /// </summary>
        /// <param name="ContainerName">the name of key container</param>
        /// <param name="includePrivateKey">whether or not to include private key in the returned key byte array</param>
        /// <returns>Key in byte array format</returns>
        protected static byte[] GetKeyFromContainer(string ContainerName, bool includePrivateKey)
        {

            RSACryptoServiceProvider.UseMachineKeyStore = true;
            // Create the CspParameters object and set the key container 
            // name used to store the RSA key pair.
            CspParameters cp = new CspParameters();
            cp.KeyContainerName = ContainerName;
            cp.Flags = CspProviderFlags.UseMachineKeyStore;

            // Create a new instance of RSACryptoServiceProvider that accesses
            // the key container MyKeyContainerName.
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(384, cp);
            return rsa.ExportCspBlob(includePrivateKey);

        }

    }
    
}
